﻿//  Copyright (c) 2010 Ray Liang (http://www.dotnetage.com)
//  Dual licensed under the MIT and GPL licenses:
//  http://www.opensource.org/licenses/mit-license.php
//  http://www.gnu.org/licenses/gpl.html

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI;
using System.Xml;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace DNA.Mvc.jQuery
{
    public class HierarchialBindingFactory<TComponent, TContainer> //: NavigationBindingFactory<TComponent, TContainer>
        where TComponent : NavigableView, IComponentItemContainer<TComponent>
        where TContainer : IComponentItemContainer<TComponent>
    {
        protected IComponentItemContainer<TComponent> Container;
        protected Func<INavigable, TComponent> creator;

        public HierarchialBindingFactory(IComponentItemContainer<TComponent> container, Func<INavigable, TComponent> factoryMethod)
        {
            Container = container;
            creator = factoryMethod;
        }

        public void Bind<T>(IHierarchicalEnumerable model, MapAction<T, NavigatableNode> mapper)
        {
            var enumer = model.GetEnumerator();
            while (enumer.MoveNext())
            {
                var navigtableItem = new NavigatableNode();
                if (!mapper.Invoke((T)enumer.Current, navigtableItem))
                    continue;
                var item = creator.Invoke(navigtableItem);

                Container.Items.Add(item);
                Container.OnItemAdded(item);

                var childen = model.GetHierarchyData(enumer.Current);
                if ((childen != null) && (childen.HasChildren))
                {
                    var factory = new HierarchialBindingFactory<TComponent, TContainer>(item, creator);
                    factory.Bind(childen.GetChildren(), mapper);
                }
            }
        }

        public void Bind(IHierarchicalNodeProvider dataProvider,bool showRoot)
        //where T : HierarchicalNode
        {
            this.Bind(dataProvider.RootNode, null,showRoot);
        }

        //public void Bind<T>(T root, Func<T, bool> dataBound)
        //    where T : HierarchicalNode
        //{
        //    this.Bind<T>(root, dataBound, true);
        //}

        public void Bind<T>(T root, Func<T, bool> dataBound, bool showRoot)
            where T : HierarchicalNode
        {
            var nodes = root.ChildNodes;
            if (root.IsAccessibleToUser())
            {
                if (dataBound != null)
                    if (!dataBound.Invoke((T)root)) return;


                if (showRoot)
                {
                    var _rootItem = creator.Invoke(root as INavigable);
                    if (root.Attributes != null)
                        _rootItem.MergeAttributes(root.Attributes);
                    Container.Items.Add(_rootItem);
                    Container.OnItemAdded(_rootItem);
                    BindChildren<T>(_rootItem, dataBound, nodes);
                }
                else
                {
                    BindChildren<T>(Container, dataBound, nodes);
                }
                
            }
        }

        private void BindChildren<T>(IComponentItemContainer<TComponent> parentNode, Func<T, bool> dataBound, List<HierarchicalNode> nodes) where T : HierarchicalNode
        {
            foreach (var node in nodes)
            {
                if (!node.IsAccessibleToUser()) continue;
                if (dataBound != null)
                    if (!dataBound.Invoke((T)node)) continue;
                var _item = creator.Invoke(node as INavigable);
                if (node.Attributes != null)
                    _item.MergeAttributes(node.Attributes);
                parentNode.Items.Add(_item);
                parentNode.OnItemAdded(_item);
                if (node.HasChildNodes)
                {
                    BindChildren<T>(_item, dataBound, node.ChildNodes);
                    //var factory = new HierarchialBindingFactory<TComponent, TContainer>(_item, creator);
                    //factory.Bind((T)node, dataBound);
                }
            }
        }

        public void BindXml(string xmlUrl,bool showRoot)
        {
            this.Bind(new XmlNodeProvider(xmlUrl),showRoot);
        }

        public void BindXml(string xmlUrl, MapAction<XmlNode, HierarchicalNode> mapper,bool showRoot)
        {
            var provider = new XmlNodeProvider(xmlUrl, mapper);
            this.Bind(provider,showRoot);
        }

        public void BindSiteMap(string siteMapUrl)
        {
            this.BindSiteMap(siteMapUrl, "/siteMap");
        }
        private UrlHelper _urlHelper = null;
        private UrlHelper Url
        {
            get
            {
                if (_urlHelper == null)
                    _urlHelper = new UrlHelper(CreateRequestContext(HttpContext.Current.Request.Url));
                return _urlHelper;
            }
        }

        private RequestContext CreateRequestContext(Uri url)
        {
            var request = new System.Web.Hosting.SimpleWorkerRequest(VirtualPathUtility.ToAppRelative(url.LocalPath).Replace("~/", ""), "", null);
            var httpContext = new HttpContextWrapper(new HttpContext(request));
            var routeData = RouteTable.Routes.GetRouteData(httpContext);
            return new RequestContext(httpContext, routeData);
        }

        public void BindSiteMap(string siteMapUrl, string selectPath)
        {
            this.BindSiteMap(siteMapUrl, selectPath, false);
        }

        public void BindSiteMap(string siteMapUrl, string selectPath,bool showRoot)
        {
            var provider = new XmlNodeProvider(siteMapUrl,
             (element, node) =>
             {
                 //IEnumerable<XAttribute> attrs = element.Attributes();
                 string controller = "";
                 string action = "";
                 //foreach (var attr in attrs)
                 for (int i = 0; i < element.Attributes.Count; i++)
                 {
                     var attr = element.Attributes[i];
                     if (attr.LocalName.Equals("title", StringComparison.OrdinalIgnoreCase)) { node.Title = attr.Value; continue; }
                     if (attr.LocalName.Equals("description", StringComparison.OrdinalIgnoreCase)) { node.Description = attr.Value; continue; }
                     if (attr.LocalName.Equals("resourceKey", StringComparison.OrdinalIgnoreCase)) { node.ResourceKey = attr.Value; continue; }
                     if (attr.LocalName.Equals("controller", StringComparison.OrdinalIgnoreCase)) { controller = attr.Value; continue; }
                     if (attr.LocalName.Equals("action", StringComparison.OrdinalIgnoreCase)) { action = attr.Value; continue; }
                     if (attr.LocalName.Equals("url", StringComparison.OrdinalIgnoreCase))
                     {
                         node.NavigateUrl = attr.Value;
                         if (VirtualPathUtility.IsAppRelative(node.NavigateUrl))
                             node.NavigateUrl = VirtualPathUtility.ToAbsolute(node.NavigateUrl);
                         continue;
                     }
                 }

                 if ((!string.IsNullOrEmpty(controller)) && (!string.IsNullOrEmpty(action)))
                     node.NavigateUrl = Url.Action(action, controller);

                 node.Item = element;
                 return true;
             });
            //provider.ElementName = "siteMapNode";
            provider.Path = selectPath;// "/siteMap";
            Bind(provider,showRoot);
        }
    }
}
